"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.

This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.

Script for converting hatch code or a hatch design into verilog, xml,
documentation, etc.
"""
#----------------------------------------------------------------------

id = "$Id: hatch.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.2/hatch/hatch.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])

#----------------------------------------------------------------------

# Try to keep this file / module as simple as possible, puting only
# top level control and the most general information here.

import optparse
from os import path

import to_hatch_structure
#from os import environ
#TOOL_DIR = environ.get('TOOL_DIR', None)   # '/home/widentifi/hatch/tools'
#if TOOL_DIR is None:
#    raise SystemExit, "Please set the 'TOOL_DIR' environment variable by sourcing hatch.bashrc."

import hatch_files
from hatch_constants import FILE_NAME, TOP_NODE
import hatch_format_translator
import hatch_log

import hatch_property_propagator    # Propagate properties into child Nodes.
import hatch_property_elaborator    # Set property values and create new properties (IO ports, etc.)
import hatch_address_mapper         # Map to physical memory.
import hatch_property_checker       # Check everything.
import hatch_info_elaborator        # Generate documentation.

import hatch_txt
import hatch_xml
import hatch_python
import hatch_verilog
import hatch_csv


parser = optparse.OptionParser(usage="usage: %prog [options] <hatch file>" \
    + "\n  Example usage:  %prog 'dir/my_design.py' -x -v -d", \
    version="%prog "+version, description="Script for hatching hardware designs.", \
    formatter=optparse.TitledHelpFormatter())

blurb = " for the hatch file(s) provided."
targets = optparse.OptionGroup(parser, "Hatch Output Target Formats", "")
targets.add_option("-t", "--txt", action="store_true", dest="txt", help="Generate text" + blurb)
targets.add_option("-x", "--xml", action="store_true", dest="xml", help="Generate xml" + blurb)
targets.add_option("-p", "--py", action="store_true", dest="py", help="Generate python" + blurb)
targets.add_option("-v", "--verilog", action="store_true", dest="verilog", help="Generate verilog" + blurb)
targets.add_option("-w", "--html", action="store_true", dest="html", help="Generate html (documentation)" + blurb)
targets.add_option("-l", "--latex", action="store_true", dest="tex", help="Generate tex (documentation)" + blurb)
targets.add_option("-s", "--csv", action="store_true", dest="csv", help="Generate csv (documentation)" + blurb)
targets.add_option("-a", "--all", action="store_true", dest="all", help="Generate all target fromats" + blurb)
parser.add_option_group(targets)

parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False, help="Log debug messages to the console.")
manualHelp = "Indicate that the hatch file has already been hatched/ has been manually edited. "
manualHelp += "Hatch will NOT run visitor objects on the design before hatching. "
manualHelp += "This is used to skip sanity checks, address mapping, etc, and is useful for quick hacks."
parser.add_option("-m", "--manual", action="store_true", dest="manual", default=False, help=manualHelp)

fileDetails = optparse.OptionGroup(parser, "Hatch File Details", "")
topNode = optparse.Option("-n", "--node", dest="topNode", metavar="STR", default=TOP_NODE)
topNode.help = "Specify the top node in the hatch design file. This defaults to the identifier '%default'."
fileDetails.add_option(topNode)
fileCountChoices = ['single','multi','default']
fileCount = optparse.Option("-c", "--count", dest="fileCount", metavar="STR", default='default')
fileCount.type, fileCount.choices = "choice", fileCountChoices
fileCount.help = "Specify the number of target files to hatch. Options are:"
fileCount.help += " 'single' hatches a single file, even for multi-file designs (many to 1)."
fileCount.help += " 'multi' hatches a target file for each design file (1 to 1)."
fileCount.help += " 'default' hatches single or multiple files for each target format, depending on each target's default behaviour."
fileDetails.add_option(fileCount)
parser.add_option_group(fileDetails)


(options, args) = parser.parse_args()
try:
    baseName, fileType = path.splitext(args[0]) # TODO check fileType.
    if len(args) > 1:
        raise IndexError, ""
except IndexError, e:
    parser.error("Specify exactly one design file for hatch to opperate on.")
# parser.destroy() 
del parser

if options.debug:
    hatch_log.consoleHandler.setLevel(hatch_log.logging.DEBUG)


# XXX This is the big line that does it all!
hatchBlock = hatch_files.hatch_file(baseName + fileType, options.topNode)


# Translate any xml (etc) into hatch code.  
# Note: No need to run property elaborator to generate unique names first.
hft = hatch_format_translator.HatchFormatTranslator()
hatchBlock = hft.translate(hatchBlock)


# Visit design before hatching.
# TODO save a new file after each visitor for debugging ?
if not options.manual:
    pp = hatch_property_propagator.HatchPropertyPropagator()
    hatchBlock.visit(pp)    # Propagate properties into child Nodes FIRST.
    pc = hatch_property_checker.HatchPropertyChecker()
    hatchBlock.visit(pc)    # Check that all properties are sane before doing more work.
    he = hatch_property_elaborator.HatchPropertyElaborator()
    hatchBlock.visit(he)    # Create any additional properties.
    am = hatch_address_mapper.Hatch_Address_Mapper()  # TODO - change the class used here.
    hatchBlock.visit(am)
    ie = hatch_info_elaborator.HatchInfoElaborator()
    hatchBlock.visit(ie)   # Generate documentation LAST.


# Get nodes relevant to hatching multiple targets.
multiFileNodes = hatch_files.get_node_files(hatchBlock)


# Build the requested target file(s).
# TODO catch & print errors & continue ?
# TODO write a function & pass in a hatcher object instead of a module!
if options.txt or options.all:   # Generate text.
    hatch_txt.generate(hatchBlock, baseName)
    if options.fileCount != 'single':   # Default is multi-file.
        for node in multiFileNodes[1:]:
            hatch_txt.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.xml or options.all: # Generate XML.
    hatch_xml.generate(hatchBlock, baseName)
    if options.fileCount == 'multi':    # Default is single-file.
        for node in multiFileNodes[1:]:
            hatch_xml.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.py or options.all:  # Generate python.
    hatch_python.generate(hatchBlock, baseName)
    if options.fileCount == 'multi':    # Default is single-file.
        for node in multiFileNodes[1:]:
            hatch_python.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.verilog or options.all: # Generate Verilog.
    hatch_verilog.generate(hatchBlock, baseName)
    if options.fileCount == 'multi':    # Default is single-file.
        for node in multiFileNodes[1:]:
            hatch_verilog.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.html:# or options.all:    # Generate HTML.
    raise NotImplementedError, "HTML not implemented yet!"
    #hatch_html.generate(hatchBlock, baseName)
    #if options.fileCount == 'multi':    # Default is single-file.
    #    for node in multiFileNodes[1:]:
    #        hatch_html.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.tex:# or options.all: # Generate LaTex.
    raise NotImplementedError, "LaTex not implemented yet!"
    #hatch_tex.generate(hatchBlock, baseName)
    #if options.fileCount == 'multi':    # Default is single-file.
    #    for node in multiFileNodes[1:]:
    #        hatch_tex.generate(node, path.splitext(node.properties[FILE_NAME])[0])
if options.csv or options.all: # Generate CSV file.
    hatch_csv.generate(hatchBlock, baseName)
    if options.fileCount == 'multi':    # Default is single-file.
        for node in multiFileNodes[1:]:
            hatch_csv.generate(node, path.splitext(node.properties[FILE_NAME])[0])

print "Done!"


